//  Copyright (c) Microsoft Corporation.  All Rights Reserved.

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Globalization;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;

namespace EAI.SAML
{
    public class EAISamlSubjectConfirmationData
    {
        DateTime notBefore = DateTime.MinValue.ToUniversalTime();
        DateTime notAfter = DateTime.MaxValue.ToUniversalTime();
        Uri recipient;
        string inResponseTo;
        string address;
        readonly ImmutableCollection<SecurityKeyIdentifier> keyInfos = new ImmutableCollection<SecurityKeyIdentifier>();
        readonly ImmutableCollection<SecurityKey> keys = new ImmutableCollection<SecurityKey>();

        bool isReadOnly;

        public EAISamlSubjectConfirmationData()
        {
        }

        public DateTime NotBefore
        {
            get { return this.notBefore; }
            set
            {
                if (this.isReadOnly)
                    throw new InvalidOperationException("The Object is ReadOnly.");

                this.notBefore = value;
            }
        }

        public DateTime NotAfter
        {
            get { return this.notAfter; }
            set
            {
                if (this.isReadOnly)
                    throw new InvalidOperationException("The Object is ReadOnly.");

                this.notAfter = value;
            }
        }

        public Uri Recipient
        {
            get { return this.recipient; }
            set
            {
                if (this.isReadOnly)
                    throw new InvalidOperationException("The Object is ReadOnly.");

                this.recipient = value;
            }
        }

        public string InResponseTo
        {
            get { return this.inResponseTo; }
            set
            {
                if (this.isReadOnly)
                    throw new InvalidOperationException("The Object is ReadOnly.");

                this.inResponseTo = value;
            }
        }

        public string Address
        {
            get { return this.address; }
            set
            {
                if (this.isReadOnly)
                    throw new InvalidOperationException("The Object is ReadOnly.");

                this.address = value;
            }
        }

        public IList<SecurityKeyIdentifier> KeyInfos
        {
            get { return this.keyInfos; }
        }

        public IList<SecurityKey> SecurityKeys
        {
            get { return this.keys; }
        }

        public void MakeReadOnly()
        {
            if (!isReadOnly)
            {
                this.keyInfos.MakeReadOnly();
                this.isReadOnly = true;
            }
        }

        public virtual void ReadXml(XmlReader reader, EAISamlSerializer serializer, SecurityTokenSerializer keyInfoSerializer, SecurityTokenResolver outOfBandTokenResolver)
        {
            if (reader == null)
                throw new ArgumentNullException("reader");

            if (reader.AttributeCount > 0)
            {
                string attributeValue = reader.GetAttribute(EAISamlConstants.NotBeforeAttribute);
                if (!String.IsNullOrEmpty(attributeValue))
                    this.notBefore = Convert.ToDateTime(attributeValue).ToUniversalTime();

                attributeValue = reader.GetAttribute(EAISamlConstants.NotOnOrAfterAttribute);
                if (!String.IsNullOrEmpty(attributeValue))
                    this.notAfter = Convert.ToDateTime(attributeValue).ToUniversalTime();

                attributeValue = reader.GetAttribute(EAISamlConstants.RecipientAttribute);
                if (!String.IsNullOrEmpty(attributeValue))
                    this.recipient = new Uri(attributeValue);

                attributeValue = reader.GetAttribute(EAISamlConstants.AddressAttribute);
                if (!String.IsNullOrEmpty(attributeValue))
                    this.address = attributeValue;
            }

            if (reader.IsEmptyElement)
            {
                reader.Read();
                reader.MoveToContent();
                return;
            }

            reader.Read();

            while (reader.IsStartElement("KeyInfo", "http://www.w3.org/2000/09/xmldsig#"))
            {
                SecurityKeyIdentifier keyIdentifier = keyInfoSerializer.ReadKeyIdentifier(reader);
                SecurityKey securityKey = null;
                foreach (SecurityKeyIdentifierClause clause in keyIdentifier)
                {
                    if (outOfBandTokenResolver.TryResolveSecurityKey(clause, out securityKey))
                        break;
                }

                if (securityKey == null)
                    throw new SecurityTokenException("Unable to resolve a Security Token Reference for the Subject.");

                keyInfos.Add(keyIdentifier);
                keys.Add(securityKey);
            }

            reader.ReadEndElement();
        }

        public virtual void WriteXml(XmlWriter writer, SecurityTokenSerializer tokenSerializer)
        {
            if (writer == null)
                throw new ArgumentNullException("writer");

            if (tokenSerializer == null)
                throw new ArgumentNullException("tokenSerializer");

            writer.WriteStartElement(EAISamlConstants.SubjectConfirmationData, EAISamlConstants.Namespace);

            if (this.notBefore != DateTime.MinValue)
                writer.WriteAttributeString(EAISamlConstants.NotBeforeAttribute, this.notBefore.ToString(EAISamlConstants.GeneratedDateTimeFormat, CultureInfo.InvariantCulture));
            if (this.notAfter != DateTime.MaxValue)
                writer.WriteAttributeString(EAISamlConstants.NotOnOrAfterAttribute, this.notAfter.ToString(EAISamlConstants.GeneratedDateTimeFormat, CultureInfo.InvariantCulture));
            if (this.recipient != null)
                writer.WriteAttributeString(EAISamlConstants.RecipientAttribute, this.recipient.ToString());
            if (this.inResponseTo != null)
                writer.WriteAttributeString(EAISamlConstants.InResponseToAttribute, this.inResponseTo);
            if (this.Address != null)
                writer.WriteAttributeString(EAISamlConstants.AddressAttribute, this.address);

            for (int i = 0; i < this.keyInfos.Count; ++i)
            {
                if (tokenSerializer.CanWriteKeyIdentifier(this.keyInfos[i]))
                    tokenSerializer.WriteKeyIdentifier(writer, this.keyInfos[i]);
            }

            writer.WriteEndElement();
        }

    }

}
